﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using EOS.Framework.Kernel;

namespace EOS.Framework.Web.Mvc
{
    public class WebPage : Container, IWebPage
    {
        private List<IWebPageFilter> _filters;

        /// <summary>
        /// Gets the filter attributes.
        /// </summary>
        /// <returns></returns>
        private List<IWebPageFilter> GetFilterAttributes()
        {
            List<IWebPageFilter> attributes = new List<IWebPageFilter>();

            object[] classAtts = this.GetType().GetCustomAttributes(typeof(IWebPageFilter), true);
            foreach (IWebPageFilter filter in classAtts)
            {
                attributes.Add(filter);
                attributes.OrderBy(f => filter.Order);
            }

            return attributes;

        }

        private bool ExecuteFilters(FilterType filterType)
        {
            bool filterFound = false;
            foreach (IWebPageFilter filter in _filters)
            {
                if (filter.FilterType == filterType)
                {
                    filterFound = true;
                    filter.Execute(Context);
                }
            }
            return filterFound;
        }

        public void Execute(PageContext pageContext)
        {
            XhtmlWriter writer = new XhtmlWriter();
            Context = pageContext;

            _filters = GetFilterAttributes();

            try
            {
                ExecuteFilters(FilterType.BeforeRequest);
                BeginRequest();

                ExecuteFilters(FilterType.BeforeAction);
                InvokeAction(pageContext);

                BeginRender();
                ExecuteFilters(FilterType.BeforeRender);
                Markup.RenderMarkup(writer, Context);
            }
            catch
            {
                if (!ExecuteFilters(FilterType.OnError))
                {
                    throw;
                }
            }

            pageContext.HttpContext.Response.Write(writer.Flush());
        }

        private void InvokeAction(PageContext pageContext)
        {
            foreach (MethodInfo method in GetType().GetMethods())
            {
                ActionAttribute[] attributes = (ActionAttribute[])method.GetCustomAttributes(typeof(ActionAttribute), true);
                
                if (attributes.Length == 1 && ((method.Name == pageContext.Action && string.IsNullOrEmpty(attributes[0].ActionName)) 
                    || attributes[0].ActionName == pageContext.Action))
                {
                    ParameterInfo[] parameters = method.GetParameters();

                    object[] parameterValues = new object[parameters.Length];
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        ParameterInfo p = parameters[i];
                        if (pageContext.Parameters.ContainsKey(p.Name))
                        {
                            try
                            {
                                parameterValues[i] = Injection.ChangeType(p.ParameterType,
                                    pageContext.Parameters[p.Name]);
                            }
                            catch (Exception)
                            {
                                parameterValues[i] = null;
                            }
                        }
                        else
                        {
                            if (p.ParameterType.IsValueType)
                            {
                                parameterValues[i] = Activator.CreateInstance(p.ParameterType);
                            }
                            else
                            {
                                parameterValues[i] = null;
                            }
                        }
                    }

                    method.Invoke(this, parameterValues);
                }
            }
        }
    }
}
